package org.lab.mtga.core.web;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.lab.mtga.core.model.Card;
import org.lab.mtga.core.model.CardType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MagicCardsParser {

	private static final Logger LOG = LoggerFactory.getLogger(MagicCardsParser.class);

	public Card parse(InputStream in) throws IOException {
		BufferedReader reader = new BufferedReader(new InputStreamReader(in));
		StringBuffer sb = new StringBuffer();
		String line;
		while ((line = reader.readLine()) != null) {
			sb.append(line).append("\n");
		}
		String html = sb.toString();
		LOG.trace(html);
		Card card = new Card();
		processPrincipal(html, card);
		processImage(html, card);
		return card;
	}

	/**
	 * Procesa las características principales de la carta a partir del siguiente formato:
	 * 
	 * <pre>
	 *         <p>Planeswalker — Liliana (Loyalty: 3), 
	 *           1BB (3)
	 *           </p>
	 *         <p class="ctext"><b>+1: Each player discards a card.<br><br>-2: Target player sacrifices a creature.<br><br>-6: Separate all permanents target player controls into two piles. That player sacrifices all permanents in the pile of his or her choice.</b></p>
	 * </pre>
	 * 
	 * @param html
	 */
	private void processPrincipal(String html, Card card) {
		Pattern pattern = Pattern.compile("<p>\\s*([\\s\\w\\d\\)\\(/\\—,\\*\\+:{}]*)\\s*</p>\\s*<p class=\"ctext\">(.*)</p>");
		Matcher matcher = pattern.matcher(html);
		if (matcher.find()) {
			processTypeAndCMC(matcher.group(1), card);
			processCardText(matcher.group(2), card);
		} else {
			LOG.warn("No se puede resolver el tipo de carta");
		}
	}

	private void processTypeAndCMC(String expression, Card card) {
		expression = expression.replaceAll("\\n|\\r", "");
		card.setSubtypes(new ArrayList<String>());
		Pattern pattern;
		Matcher matcher;
		pattern = Pattern.compile(",\\s*([BGRUW\\d\\{\\}]+)\\s*\\((\\d+)\\)");
		matcher = pattern.matcher(expression);
		if (matcher.find()) {
			card.setCmcString(matcher.group(1));
			card.setCmcCount(Integer.parseInt(matcher.group(2)));
			expression = matcher.replaceFirst("");
		}
		pattern = Pattern.compile("^\\s*Basic Land\\s*$");
		matcher = pattern.matcher(expression);
		if (matcher.matches()) {
			card.addCardType(CardType.Land);
			card.addCardType(CardType.BasicLand);
			expression = matcher.replaceAll("");
		}
		pattern = Pattern.compile("^\\s*Land\\s*$");
		matcher = pattern.matcher(expression);
		if (matcher.matches()) {
			card.addCardType(CardType.Land);
		}
		pattern = Pattern.compile("^\\s*Legendary Land\\s*$");
		matcher = pattern.matcher(expression);
		if (matcher.matches()) {
			card.setLegendary(true);
			card.addCardType(CardType.Land);
			expression = matcher.replaceAll("");
		}
		pattern = Pattern.compile("^\\s*Land\\s*—(.+)$");
		matcher = pattern.matcher(expression);
		if (matcher.matches()) {
			card.addCardType(CardType.Land);
			expression = matcher.replaceAll("");
		}
		pattern = Pattern.compile("Instant");
		matcher = pattern.matcher(expression);
		if (matcher.find()) {
			card.addCardType(CardType.Instant);
			expression = matcher.replaceAll("");
		}
		pattern = Pattern.compile("Sorcery");
		matcher = pattern.matcher(expression);
		if (matcher.find()) {
			card.addCardType(CardType.Sorcery);
			expression = matcher.replaceAll("");
		}
		pattern = Pattern.compile("Enchantment");
		matcher = pattern.matcher(expression);
		if (matcher.find()) {
			card.addCardType(CardType.Enchantment);
			expression = matcher.replaceAll("");
		}
		pattern = Pattern.compile("Artifact");
		matcher = pattern.matcher(expression);
		if (matcher.find()) {
			card.addCardType(CardType.Artifact);
			expression = matcher.replaceAll("");
		}

		pattern = Pattern.compile("Creature\\s*—\\s*([\\w\\s]+)\\s*(\\w+)\\/(\\w+)\\s*");
		matcher = pattern.matcher(expression);
		if (matcher.find()) {
			String subtype = matcher.group(1);
			card.getSubtypes().add(subtype);
			card.setPower(matcher.group(2));
			card.setToughness(matcher.group(3));
			expression = matcher.replaceFirst("");
			card.addCardType(CardType.Creature);
		}
		pattern = Pattern.compile("Planeswalker\\s*—\\s*(\\w+)\\s*\\(Loyalty:\\s*(\\d+)\\)");
		matcher = pattern.matcher(expression);
		if (matcher.find()) {
			String plane = matcher.group(1);
			card.getSubtypes().add(plane);
			card.setLoyalty(Integer.parseInt(matcher.group(2)));
			expression = matcher.replaceFirst("");
			card.addCardType(CardType.Planeswalker);
		}
		if (card.getTypes().isEmpty()) {
			LOG.warn("No se puede determinar el tipo de carta a partir de: '{}'", expression);
		}
	}

	private void processCardText(String expression, Card card) {
		card.setText(expression.replaceAll("(<br>)+|<b>|</b>", "\n"));
	}

	/**
	 * Procesa la URL de la imagen de la carta a partir del siguiente formato:
	 * 
	 * <pre>
	 * <img src="http://magiccards.info/scans/en/isd/105.jpg" alt="Liliana of the Veil" width="312" height="445" style="border: 1px solid black;">
	 * </pre>
	 */
	private void processImage(String html, Card card) {
		Pattern pattern = Pattern.compile("<img src=\"([\\w:/\\.]+)\"\\s+alt=\"[\\w\\s]+\" width=\"312\"");
		Matcher matcher = pattern.matcher(html);
		if (matcher.find()) {
			card.setExternalImageUrl(matcher.group(1));
		}
	}
}
